home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / shells / scsh-0.4 / scsh-0 / scsh-0.4.2 / scsh / scsh.scm < prev    next >
Text File  |  1995-10-28  |  23KB  |  733 lines

  1. ;;; A Scheme shell.
  2. ;;; Copyright (c) 1992 by Olin Shivers.
  3. ;;; Copyright (c) 1994 by Brian D. Carlstrom.
  4.  
  5. ;;; Call THUNK, then die.
  6. ;;; A clever definition in a clever implementation allows the caller's stack
  7. ;;; and dynamic env to be gc'd away, since this procedure never returns.
  8.  
  9. (define (call-terminally thunk)
  10.   (with-continuation #f (lambda () (thunk) (exit 0))))
  11.   ;; Alternatively: (with-continuation #f thunk)
  12.  
  13. ;;; More portably, but less usefully:
  14. ;;; (define (call-terminally thunk)
  15. ;;;   (thunk)
  16. ;;;   (exit 0))
  17.  
  18. ;;; Like FORK, but the parent and child communicate via a pipe connecting
  19. ;;; the parent's stdin to the child's stdout. This function side-effects
  20. ;;; the parent by changing his stdin.
  21.  
  22. (define (fork/pipe . maybe-thunk)
  23.   (really-fork/pipe fork maybe-thunk))
  24.  
  25. (define (%fork/pipe . maybe-thunk)
  26.   (really-fork/pipe %fork maybe-thunk))
  27.   
  28. ;;; Common code for FORK/PIPE and %FORK/PIPE.
  29. (define (really-fork/pipe forker maybe-thunk)
  30.   (receive (r w) (pipe)
  31.     (let ((proc (forker)))
  32.       (cond (proc        ; Parent
  33.          (close w)
  34.          (move->fdes r 0))
  35.         (else        ; Child
  36.          (close r)
  37.          (move->fdes w 1)
  38.          (if (pair? maybe-thunk)
  39.          (call-terminally (car maybe-thunk)))))
  40.       proc)))
  41.  
  42.  
  43. ;;; FORK/PIPE with a connection list.
  44. ;;; (FORK/PIPE . m-t) = (apply fork/pipe+ '((1 0)) m-t)
  45.  
  46. (define (%fork/pipe+ conns . maybe-thunk)
  47.   (really-fork/pipe+ %fork conns maybe-thunk))
  48.  
  49. (define (fork/pipe+ conns . maybe-thunk)
  50.   (really-fork/pipe+ fork conns maybe-thunk))
  51.  
  52. ;;; Common code.
  53. (define (really-fork/pipe+ forker conns maybe-thunk)
  54.   (let* ((pipes (map (lambda (conn) (call-with-values pipe cons))
  55.              conns))
  56.      (rev-conns (map reverse conns))
  57.      (froms (map (lambda (conn) (reverse (cdr conn)))
  58.              rev-conns))
  59.      (tos (map car rev-conns)))
  60.  
  61.     (let ((proc (forker)))
  62.       (cond (proc            ; Parent
  63.          (for-each (lambda (to r/w)
  64.              (let ((w (cdr r/w))
  65.                    (r (car r/w)))
  66.                (close w)
  67.                (move->fdes r to)))
  68.                tos pipes))
  69.  
  70.         (else        ; Child
  71.          (for-each (lambda (from r/w)
  72.              (let ((r (car r/w))
  73.                    (w (cdr r/w)))
  74.                (close r)
  75.                (for-each (lambda (fd) (dup w fd)) from)
  76.                (close w))) ; Unrevealed ports win.
  77.                froms pipes)
  78.          (if (pair? maybe-thunk)
  79.          (call-terminally (car maybe-thunk)))))
  80.       proc)))
  81.  
  82. (define (tail-pipe a b)
  83.   (fork/pipe a)
  84.   (call-terminally b))
  85.  
  86. (define (tail-pipe+ conns a b)
  87.   (fork/pipe+ conns a)
  88.   (call-terminally b))
  89.  
  90. ;;; Lay a pipeline, one process for each thunk. Last thunk is called
  91. ;;; in this process. PIPE* never returns.
  92.  
  93. (define (pipe* . thunks)
  94.   (letrec ((lay-pipe (lambda (thunks)
  95.                (let ((thunk (car thunks))
  96.                  (thunks (cdr thunks)))
  97.              (if (pair? thunks)
  98.                  (begin (fork/pipe thunk)
  99.                     (lay-pipe thunks))
  100.                  (call-terminally thunk)))))) ; Last one.
  101.     (if (pair? thunks)
  102.     (lay-pipe thunks)
  103.     (error "No thunks passed to PIPE*"))))
  104.  
  105. ;;; Splice the processes into the i/o flow upstream from us.
  106. ;;; First thunk's process reads from our stdin; last thunk's process'
  107. ;;; output becomes our new stdin. Essentially, n-ary fork/pipe.
  108. ;;;
  109. ;;; This procedure is so trivial it isn't included.
  110. ;;; (define (pipe-splice . thunks) (for-each fork/pipe thunks))
  111.  
  112.  
  113.  
  114. ;;; Environment stuff
  115. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  116.  
  117. ;;; These two functions are obsoleted by the more general INFIX-SPLITTER and
  118. ;;; JOIN-STRINGS functions. However, we keep SPLIT-COLON-LIST defined
  119. ;;; internally so the top-level startup code (INIT-SCSH) can use it
  120. ;;; to split up $PATH without requiring the field-splitter or regexp code.
  121.  
  122. (define (split-colon-list clist)
  123.   (let ((len (string-length clist)))
  124.     (if (= 0 len) '()            ; Special case "" -> ().
  125.  
  126.     ;; Main loop.
  127.     (let split ((i 0))
  128.       (cond ((index clist #\: i) =>
  129.          (lambda (colon)
  130.            (cons (substring clist i colon)
  131.              (split (+ colon 1)))))
  132.         (else (list (substring clist i len))))))))
  133.  
  134. ;;; Unix colon lists typically use colons as separators, which
  135. ;;; is not as clean to deal with as terminators, but that's Unix.
  136. ;;; Note ambiguity: (s-l->c-l '()) = (s-l->c-l '("")) = "".
  137.  
  138. ; (define (string-list->colon-list slist)
  139. ;   (if (pair? slist)
  140. ;       (apply string-append
  141. ;          (let colonise ((lis slist))    ; LIS is always
  142. ;            (let ((tail (cdr lis)))         ; a pair.
  143. ;          (cons (car lis)
  144. ;                (if (pair? tail)
  145. ;                (cons ":" (colonise tail))
  146. ;                '())))))
  147. ;       ""))    ; () case.
  148.  
  149.  
  150. (define (alist-delete key alist)
  151.   (filter (lambda (key/val) (not (equal? key (car key/val)))) alist))
  152.  
  153. (define (alist-update key val alist)
  154.   (cons (cons key val)
  155.     (alist-delete key alist)))
  156.  
  157. ;;; Remove shadowed entries from ALIST. Preserves element order.
  158. ;;; (This version shares no structure.)
  159.  
  160. (define (alist-compress alist) 
  161.   (reverse (let compress ((alist alist) (ans '()))
  162.          (if (pair? alist)
  163.          (let ((key/val (car alist))
  164.                (alist (cdr alist)))
  165.            (compress alist (if (assoc (car key/val) ans) ans
  166.                        (cons key/val ans))))
  167.          ans))))
  168.  
  169. ;; Tail-recursive loops suck.
  170. ;; (define (alist-compress alist)
  171. ;;   (loop (initial (ans '()))
  172. ;;       (for key/val in alist)
  173. ;;   
  174. ;;       (when (not (assoc (car key/val) ans)))
  175. ;;       (next (ans (cons key/val ans)))
  176. ;;   
  177. ;;       (result (reverse ans))))
  178.  
  179. (define (add-before elt before list)
  180.   (let rec ((list list))
  181.     (if (pair? list)
  182.     (let ((x (car list)))
  183.       (if (equal? x before)
  184.           (cons elt list)
  185.           (cons x (rec (cdr list)))))
  186.     (cons elt list))))
  187.  
  188. ;;; In ADD-AFTER, the labelled LET adds ELT after the last occurrence of AFTER
  189. ;;; in LIST, and returns the list. However, if the LET finds no occurrence 
  190. ;;; of AFTER in LIST, it returns #F instead.
  191.  
  192. (define (add-after elt after list)
  193.   (or (let rec ((list list))
  194.     (if (pair? list)
  195.         (let* ((x (car list))
  196.            (tail (cdr list))
  197.            (ans (rec tail))) ; #f if AFTER wasn't encountered.
  198.           (cond (ans (cons x ans))
  199.             ((equal? x after)
  200.              (cons x (cons elt tail)))
  201.             (else #f)))        ; AFTER doesn't appear in LIST.
  202.         #f))            ; AFTER doesn't appear in LIST.
  203.       (cons elt list))) 
  204.  
  205. ;;; Or, just say...
  206. ;;; (reverse (add-before elt after (reverse list)))
  207.  
  208. (define (with-env* alist-delta thunk)
  209.   (let* ((old-env #f)
  210.      (new-env (reduce (lambda (alist key/val)
  211.                 (alist-update (car key/val) (cdr key/val) alist))
  212.               (env->alist)
  213.               alist-delta)))
  214.     (dynamic-wind
  215.       (lambda ()
  216.     (set! old-env (env->alist))
  217.     (alist->env new-env))
  218.       thunk
  219.       (lambda ()
  220.     (set! new-env (env->alist))
  221.     (alist->env old-env)))))
  222.  
  223. (define (with-total-env* alist thunk)
  224.   (let ((old-env (env->alist)))
  225.     (dynamic-wind
  226.       (lambda ()
  227.     (set! old-env (env->alist))
  228.     (alist->env alist))
  229.       thunk
  230.       (lambda ()
  231.     (set! alist (env->alist))
  232.     (alist->env old-env)))))
  233.  
  234.  
  235. (define (with-cwd* dir thunk)
  236.   (let ((old-wd #f))
  237.     (dynamic-wind
  238.       (lambda ()
  239.     (set! old-wd (cwd))
  240.     (chdir dir))
  241.       thunk
  242.       (lambda ()
  243.     (set! dir (cwd))
  244.     (chdir old-wd)))))
  245.  
  246. (define (with-umask* mask thunk)
  247.   (let ((old-mask #f))
  248.     (dynamic-wind
  249.       (lambda ()
  250.     (set! old-mask (umask))
  251.     (set-umask mask))
  252.       thunk
  253.       (lambda ()
  254.     (set! mask (umask))
  255.     (set-umask old-mask)))))
  256.  
  257. ;;; Sugar:
  258.  
  259. (define-simple-syntax (with-cwd dir . body)
  260.   (with-cwd* dir (lambda () . body)))
  261.  
  262. (define-simple-syntax (with-umask mask . body)
  263.   (with-umask* mask (lambda () . body)))
  264.  
  265. (define-simple-syntax (with-env delta . body)
  266.   (with-env* `delta (lambda () . body)))
  267.  
  268. (define-simple-syntax (with-total-env env . body)
  269.   (with-total-env* `env (lambda () . body)))
  270.  
  271.  
  272. (define (call/temp-file writer user)
  273.   (let ((fname #f))
  274.     (dynamic-wind
  275.       (lambda () (if fname (error "Can't wind back into a CALL/TEMP-FILE")
  276.              (set! fname (create-temp-file))))
  277.       (lambda ()
  278.     (with-output-to-file fname writer)
  279.     (user fname))
  280.       (lambda () (if fname (delete-file fname))))))
  281.  
  282. ;;; Create a new temporary file and return its name.
  283. ;;; The optional argument specifies the filename prefix to use, and defaults
  284. ;;; to "/usr/tmp/<pid>.", where <pid> is the current process' id. The procedure
  285. ;;; scans through the files named <prefix>0, <prefix>1, ... until it finds a
  286. ;;; filename that doesn't exist in the filesystem. It creates the file with 
  287. ;;; permission #o600, and returns the filename.
  288. ;;; 
  289.  
  290. (define (create-temp-file . maybe-prefix)
  291.   (let ((oflags (bitwise-ior open/write
  292.                  (bitwise-ior open/create open/exclusive))))
  293.     (apply temp-file-iterate
  294.        (lambda (fname)
  295.          (close-fdes (open-fdes fname oflags #o600))
  296.          fname)
  297.        (if (null? maybe-prefix) '()
  298.            (list (string-append (car maybe-prefix) ".~a"))))))
  299.  
  300. (define *temp-file-template*
  301.   (make-fluid (string-append "/usr/tmp/" (number->string (pid)) ".~a")))
  302.  
  303.  
  304. (define (temp-file-iterate maker . maybe-template)
  305.   (let ((template (optional-arg maybe-template (fluid *temp-file-template*))))
  306.     (let loop ((i 0))
  307.       (if (> i 1000) (error "Can't create temp-file")
  308.       (let ((fname (format #f template (number->string i))))
  309.         (receive retvals (with-errno-handler
  310.                    ((errno data)
  311.                 ((errno/exist) #f))
  312.                    (maker fname))
  313.           (if (car retvals) (apply values retvals)
  314.           (loop (+ i 1)))))))))
  315.  
  316.  
  317.  
  318. ;;; Roughly equivalent to (pipe).
  319. ;;; Returns two file ports [iport oport] open on a temp file.
  320. ;;; Use this when you may have to buffer large quantities between
  321. ;;; writing and reading. Note that if the consumer gets ahead of the
  322. ;;; producer, it won't hang waiting for input, it will just return
  323. ;;; EOF. To play it safe, make sure that the producer runs to completion
  324. ;;; before starting the consumer.
  325. ;;;
  326. ;;; The temp file is deleted before TEMP-FILE-CHANNEL returns, so as soon
  327. ;;; as the ports are closed, the file's disk storage is reclaimed.
  328.  
  329. (define (temp-file-channel)
  330.   (let* ((fname (create-temp-file))
  331.      (iport (open-input-file fname))
  332.      (oport (open-output-file fname)))
  333.     (delete-file fname)
  334.     (values iport oport)))
  335.     
  336.  
  337. ;; Return a Unix port such that reads on it get the chars produced by
  338. ;; DISPLAYing OBJ. For example, if OBJ is a string, then reading from
  339. ;; the port produces the characters of OBJ.
  340. ;; 
  341. ;; This implementation works by writing the string out to a temp file,
  342. ;; but that isn't necessary. It could work, for example, by forking off a 
  343. ;; writer process that outputs to a pipe, i.e.,
  344. ;;     (run/port (begin (display obj (fdes->outport 1))))
  345.  
  346. (define (open-string-source obj)
  347.   (receive (inp outp) (temp-file-channel)
  348.     (display obj outp)
  349.     (close-output-port outp)
  350.     inp))
  351.  
  352.  
  353. ;;;; Process->Scheme interface forms: run/collecting, run/port, run/string, ...
  354.  
  355. ;;; (run/collecting FDS . EPF)
  356. ;;; --------------------------
  357. ;;; RUN/COLLECTING and RUN/COLLECTING* run processes that produce multiple
  358. ;;; output streams and return ports open on these streams.
  359. ;;;
  360. ;;; To avoid issues of deadlock, RUN/COLLECTING first runs the process
  361. ;;; with output to temp files, then returns the ports open on the temp files.
  362. ;;;
  363. ;;; (run/collecting (1 2) (ls))
  364. ;;; runs ls with stdout (fd 1) and stderr (fd 2) redirected to temporary files.
  365. ;;; When ls is done, RUN/COLLECTING returns two ports open on the temporary
  366. ;;; files. The files are deleted before RUN/COLLECTING returns, so when
  367. ;;; the ports are closed, they vanish.
  368. ;;;
  369. ;;; The FDS list of file descriptors is implicitly backquoted.
  370. ;;;
  371. ;;; RUN/COLLECTING* is the procedural abstraction of RUN/COLLECTING.
  372.  
  373. (define (run/collecting* fds thunk)
  374.   ;; First, generate a pair of ports for each communications channel.
  375.   ;; Each channel buffers through a temp file.
  376.   (let* ((channels (map (lambda (ignore)
  377.               (call-with-values temp-file-channel cons))
  378.                fds))
  379.      (read-ports (map car channels))
  380.      (write-ports (map cdr channels))
  381.  
  382.      ;; In a subprocess, close the read ports, redirect input from
  383.      ;; the write ports, and run THUNK.
  384.      (status (run (begin (for-each close-input-port read-ports)
  385.                  (for-each move->fdes write-ports fds)
  386.                  (thunk)))))
  387.  
  388.     ;; In this process, close the write ports and return the exit status
  389.     ;; and all the the read ports.
  390.     (for-each close-output-port write-ports)
  391.     (apply values status read-ports)))
  392.  
  393.  
  394. ;;; Single-stream collectors:
  395. ;;; Syntax: run/port, run/file, run/string, run/strings, run/sexp, run/sexps
  396. ;;; Procedures: run/port*, run/file*, run/string*, run/strings*, run/sexp*,
  397. ;;;             run/sexps*
  398. ;;;             port->string, port->string-list, port->sexp-list, 
  399. ;;;             port->list
  400. ;;; 
  401. ;;; Syntax:
  402. ;;; (run/port . epf)
  403. ;;;     Fork off the process EPF and return a port on its stdout.
  404. ;;; (run/file . epf)
  405. ;;;     Run process EPF with stdout redirected into a temp file.
  406. ;;;     When the process exits, return the name of the file.
  407. ;;; (run/string . epf)
  408. ;;;     Read the process' stdout into a string and return it.
  409. ;;; (run/strings . epf)
  410. ;;;     Run process EPF, reading newline-terminated strings from its stdout
  411. ;;;     until EOF. After process exits, return list of strings read. Delimiting
  412. ;;;    newlines are trimmed from the strings.
  413. ;;; (run/sexp . epf)
  414. ;;;     Run process EPF, read and return one sexp from its stdout with READ.
  415. ;;; (run/sexps . epf)
  416. ;;;     Run process EPF, read sexps from its stdout with READ until EOF.
  417. ;;;    After process exits, return list of items read.
  418. ;;;
  419. ;;; Procedural abstractions:
  420. ;;; run/port*, run/file*, run/string*, run/strings*, run/sexp*, run/sexps*
  421. ;;;
  422. ;;; These are all procedural equivalents for the macros. They all take
  423. ;;; one argument: the process to be executed passed as a thunk. For example,
  424. ;;; (RUN/PORT . epf) expands into (RUN/PORT* (LAMBDA () (EXEC-EPF . epf)))
  425. ;;;
  426. ;;; Other useful procedures:
  427. ;;; 
  428. ;;; (port->string port) 
  429. ;;;     Read characters from port until EOF; return string collected.
  430. ;;; (port->string-list port)
  431. ;;;     Read newline-terminated strings from port until EOF. Return
  432. ;;;     the list of strings collected.
  433. ;;; (port->sexp-list port)
  434. ;;;     Read sexps from port with READ until EOF. Return list of items read.
  435. ;;; (port->list reader port)
  436. ;;;     Repeatedly applies READER to PORT, accumulating results into a list.
  437. ;;;     On EOF, returns the list of items thus collected.
  438. ;;; (reduce-port port reader op . seeds)
  439. ;;;     Repeatedly read things from PORT with READER. Each time you read
  440. ;;;     some value V, compute a new set of seeds with (apply OP V SEEDS).
  441. ;;;     (More than 1 seed means OP must return multiple values).
  442. ;;;     On eof, return the seeds.
  443. ;;;     PORT->LIST is just (REDUCE-PORT PORT READ CONS '())
  444.  
  445. (define (run/port+proc* thunk)
  446.   (receive (r w) (pipe)
  447.     (let ((proc (fork (lambda ()
  448.             (close r)
  449.             (move->fdes w 1)
  450.             (with-current-output-port* w thunk)))))
  451.       (close w)
  452.       (values r proc))))
  453.  
  454. (define (run/port* thunk)
  455.   (receive (port proc) (run/port+proc* thunk)
  456.     port))
  457.  
  458. (define (run/file* thunk)
  459.   (let ((fname (create-temp-file)))
  460.     (run (begin (thunk)) (> ,fname))
  461.     fname))
  462.  
  463. (define (run/string* thunk) 
  464.   (close-after (run/port* thunk) port->string))
  465.  
  466. (define (run/sexp* thunk)
  467.   (close-after (run/port* thunk) read))
  468.  
  469. (define (run/sexps* thunk)
  470.   (close-after (run/port* thunk) port->sexp-list))
  471.  
  472. (define (run/strings* thunk)
  473.   (close-after (run/port* thunk) port->string-list))
  474.  
  475.  
  476. ;;; Read characters from PORT until EOF, collect into a string.
  477.  
  478. (define (port->string port)
  479.   (let ((sc (make-string-collector)))
  480.     (letrec ((lp (lambda ()
  481.            (cond ((read-string 1024 port) =>
  482.               (lambda (s)
  483.                 (collect-string! sc s)
  484.                 (lp)))
  485.              (else (string-collector->string sc))))))
  486.       (lp))))
  487.  
  488. ;;; (loop (initial (sc (make-string-collector)))
  489. ;;;       (bind (s (read-string 1024 port)))
  490. ;;;       (while s)
  491. ;;;       (do (collect-string! sc s))
  492. ;;;       (result (string-collector->string sc)))
  493.  
  494. ;;; Read items from PORT with READER until EOF. Collect items into a list.
  495.  
  496. (define (port->list reader port)
  497.   (let lp ((ans '()))
  498.     (let ((x (reader port)))
  499.       (if (eof-object? x) (reverse! ans)
  500.       (lp (cons x ans))))))
  501.  
  502. (define (port->sexp-list port)
  503.   (port->list read port))
  504.  
  505. (define (port->string-list port)
  506.   (port->list read-line port))
  507.  
  508. (define (reduce-port port reader op . seeds)
  509.   (letrec ((reduce (lambda seeds
  510.              (let ((x (reader port)))
  511.                (if (eof-object? x) (apply values seeds)
  512.                (call-with-values (lambda () (apply op x seeds))
  513.                          reduce))))))
  514.     (apply reduce seeds)))
  515.  
  516. ;;; Not defined:
  517. ;;; (field-reader field-delims record-delims)
  518. ;;; Returns a reader that reads strings delimited by 1 or more chars from
  519. ;;; the string FIELD-DELIMS. These strings are collected in a list until
  520. ;;; eof or until 1 or more chars from RECORD-DELIMS are read. Then the
  521. ;;; accumulated list of strings is returned. For example, if we want
  522. ;;; a procedure that reads one line of input, splitting it into 
  523. ;;; whitespace-delimited strings, we can use 
  524. ;;;     (field-reader " \t" "\n")
  525. ;;; for a reader.
  526.  
  527.  
  528.  
  529. ;; Loop until EOF reading characters or strings and writing (FILTER char)
  530. ;; or (FILTER string). Useful as an arg to FORK or FORK/PIPE.
  531.  
  532. (define (char-filter filter)
  533.   (lambda ()
  534.     (let lp ()
  535.       (let ((c (read-char)))
  536.     (if (not (eof-object? c))
  537.         (begin (write-char (filter c))
  538.            (lp)))))))
  539.  
  540. (define (string-filter filter . maybe-buflen)
  541.   (let* ((buflen (optional-arg maybe-buflen 1024))
  542.      (buf (make-string buflen)))
  543.     (lambda ()
  544.       (let lp ()
  545.     (cond ((read-string! buf 0 buflen) =>
  546.            (lambda (nread)
  547.          (display (filter (if (= nread buflen) buf
  548.                       (substring buf 0 nread)))) ; last one.
  549.          (lp))))))))
  550.  
  551.  
  552. ;;; Stdio/stdport sync procedures
  553. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  554.  
  555. (define (stdio->stdports)
  556.   (set-current-input-port!  (fdes->inport 0))
  557.   (set-current-output-port! (fdes->inport 1))
  558.   (set-error-output-port!   (fdes->inport 2)))
  559.  
  560. (define (with-stdio-ports* thunk)
  561.   (with-current-input-port (fdes->inport 0)
  562.     (with-current-output-port (fdes->outport 1)
  563.       (with-error-output-port (fdes->outport 2)
  564.     (thunk)))))
  565.  
  566. (define-simple-syntax (with-stdio-ports body ...)
  567.   (with-stdio-ports* (lambda () body ...)))
  568.  
  569.  
  570. (define (stdports->stdio)
  571.   (dup (current-input-port)  0)
  572.   (dup (current-output-port) 1)
  573.   (dup (error-output-port)   2))
  574.  
  575.  
  576. ;;; Command-line argument access
  577. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  578.  
  579. ;;; Some globals.
  580. (define %command-line '())        ; Includes program.
  581. (define command-line-arguments #f)    ; Doesn't include program.
  582.  
  583. (define (set-command-line-args! args)
  584.   (set! %command-line args)
  585.   (set! command-line-arguments (append (cdr args) '())))
  586.  
  587. (define (arg* arglist n . maybe-default-thunk)
  588.   (let ((oops (lambda () (error "argument out of bounds" arglist n))))
  589.     (if (< n 1) (oops)
  590.     (let lp ((al arglist) (n n))
  591.       (if (pair? al)
  592.           (if (= n 1) (car al)
  593.           (lp (cdr al) (- n 1)))
  594.           (if (and (pair? maybe-default-thunk)
  595.                (null? (cdr maybe-default-thunk)))
  596.           ((car maybe-default-thunk))
  597.           (oops)))))))
  598.  
  599. (define (arg arglist n . maybe-default)
  600.   (if maybe-default (arg* arglist n (lambda () (car maybe-default)))
  601.       (arg* arglist n)))
  602.  
  603. (define (argv n . maybe-default)
  604.   (apply arg (cdr %command-line) n maybe-default))
  605.  
  606. (define (command-line) (append %command-line '()))
  607.  
  608. ;;; EXEC support
  609. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  610. ;;; Assumes a low-level %exec procedure:
  611. ;;; (%exec prog arglist env)
  612. ;;;   ENV is either #t, meaning the current environment, or a string->string
  613. ;;;       alist.
  614. ;;;   %EXEC stringifies PROG and the elements of ARGLIST.
  615.  
  616. (define (stringify thing)
  617.   (cond ((string? thing) thing)
  618.     ((symbol? thing)
  619.      (symbol->string thing))
  620. ;    ((symbol? thing)
  621. ;     (list->string (map char-downcase
  622. ;                (string->list (symbol->string thing)))))
  623.     ((integer? thing)
  624.      (number->string thing))
  625.     (else (error "Can only stringify strings, symbols, and integers."
  626.              thing))))
  627.  
  628. (define (exec-path-search prog path-list)
  629.   (if (file-name-absolute? prog)
  630.       (and (file-executable? prog) prog)
  631.       (first? (lambda (dir)
  632.         (let ((fname (string-append dir "/" prog)))
  633.           (and (file-executable? fname) fname)))
  634.          path-list)))
  635.             
  636. (define (exec/env prog env . arglist)
  637.   (flush-all-ports)
  638.   (%exec prog (cons prog arglist) env))
  639.  
  640. ;(define (exec-path/env prog env . arglist)
  641. ;  (cond ((exec-path-search (stringify prog) exec-path-list) =>
  642. ;     (lambda (binary)
  643. ;       (apply exec/env binary env arglist)))
  644. ;    (else (error "No executable found." prog arglist))))
  645.  
  646. ;;; This procedure is bummed by tying in directly to %%exec/errno
  647. ;;; and pulling some of %exec's code out of the inner loop so that
  648. ;;; the inner loop will be fast. Folks don't like waiting...
  649.  
  650. (define (exec-path/env prog env . arglist)
  651.   (flush-all-ports)
  652.   (let ((prog (stringify prog)))
  653.     (if (index prog #\/)
  654.  
  655.     ;; Contains a slash -- no path search.
  656.     (%exec prog (cons prog arglist) env)
  657.  
  658.     ;; Try each directory in PATH-LIST.
  659.     (let ((argv (list->vector (cons prog (map stringify arglist)))))
  660.       (cloexec-unrevealed-ports)
  661.       (for-each (lambda (dir)
  662.               (let ((binary (string-append dir "/" prog)))
  663.             (%%exec/errno binary argv env)))
  664.             exec-path-list))))
  665.  
  666.     (error "No executable found." prog arglist))
  667.      
  668. (define (exec-path prog . arglist)
  669.   (apply exec-path/env prog #t arglist))
  670.  
  671. (define (exec prog . arglist)
  672.   (apply exec/env prog #t arglist))
  673.  
  674.  
  675. ;;; Assumes niladic primitive %%FORK.
  676.  
  677. (define (fork . maybe-thunk)
  678.   (flush-all-ports)
  679.   (really-fork #t maybe-thunk))
  680.  
  681. (define (%fork . maybe-thunk)
  682.   (really-fork #f maybe-thunk))
  683.  
  684. (define (really-fork clear-interactive? maybe-thunk)
  685.   (let ((pid (%%fork)))
  686.     (cond ((zero? pid)                ; Child
  687.        (set! reaped-procs '())
  688.        (if clear-interactive?
  689.            (set-batch-mode?! #t))    ; Children are non-interactive.
  690.        (and (pair? maybe-thunk)
  691.         (call-terminally (car maybe-thunk))))
  692.       (else (new-child-proc pid)))))    ; Parent
  693.  
  694.  
  695. (define (exit . maybe-status)
  696.   (flush-all-ports)
  697.   (exit/errno (optional-arg  maybe-status 0))
  698.   (display "The evil undead walk the earth." 2)
  699.   (error "(exit) returned."))
  700.  
  701.  
  702. ;;; The classic T 2.0 primitive.
  703. ;;; This definition works for procedures running on top of Unix systems.
  704. (define (halts? proc) #t)
  705.  
  706.  
  707. ;;; Low-level init absolutely required for any scsh program.
  708.  
  709. (define (init-scsh-hindbrain relink-ff?)
  710.   (if relink-ff? (lookup-all-externals)) ; Re-link C calls.
  711.   (init-fdports!))
  712.  
  713.  
  714. ;;; Some globals:
  715. (define home-directory "")
  716. (define exec-path-list '())
  717.  
  718. (define (init-scsh-vars quietly?)
  719.   (set! home-directory
  720.     (cond ((getenv "HOME") => ensure-file-name-is-nondirectory)
  721.           (else (if (not quietly?)
  722.             (warn "Starting up with no home directory ($HOME)."))
  723.             "/")))
  724.   (set! exec-path-list
  725.     (cond ((getenv "PATH") => split-colon-list)
  726.           (else (if (not quietly?)
  727.             (warn "Starting up with no path ($PATH)."))
  728.             '()))))
  729.  
  730.  
  731. ; SIGTSTP blows s48 away. ???
  732. (define (suspend) (signal-process 0 signal/stop))
  733.